//
// Created by Administrator on 2023/9/2.
// ILI9325 显示屏
//

#include "lcd.h"
#include "main.h"
#include "debug.h"


uint16_t POINT_COLOR = LCD_COLOR_BLUE;	//画笔颜色

/* 管理LCD重要参数 */
_lcd_dev lcddev;

/**
 * @brief       LCD写数据
 * @param       data: 要写入的数据
 * @retval      无
 */
void LCD_WR_DATA(volatile uint16_t data) {
    data = data;            /* 使用-O2优化的时候,必须插入的延时 */
    LCD->LCD_RAM = data;
}

/**
 * @brief       LCD写寄存器编号/地址函数
 * @param       regno: 寄存器编号/地址
 * @retval      无
 */
void LCD_WR_REG(volatile uint16_t regno) {
    regno = regno;          /* 使用-O2优化的时候,必须插入的延时 */
    LCD->LCD_REG = regno;   /* 写入要写的寄存器序号 */
}

/**
 * @brief       LCD写寄存器
 * @param       regno:寄存器编号/地址
 * @param       data:要写入的数据
 * @retval      无
 */
void LCD_WriteReg(uint16_t regno, uint16_t data) {
    LCD->LCD_REG = regno;   /* 写入要写的寄存器序号 */
    LCD->LCD_RAM = data;    /* 写入数据 */
}

/**
 * @brief       LCD读数据
 * @param       无
 * @retval      读取到的数据
 */
static uint16_t LCD_RD_DATA(void) {
    volatile uint16_t ram;  /* 防止被优化 */
    ram = LCD->LCD_RAM;
    return ram;
}

/**
 * @brief       准备写GRAM
 * @param       无
 * @retval      无
 */
void LCD_WriteRAM_Prepare(void) {
    LCD->LCD_REG = lcddev.wramcmd;
}
//设置光标位置
//Xpos:横坐标
//Ypos:纵坐标
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
    if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏其实就是调转x,y坐标
    LCD_WriteReg(lcddev.setxcmd, Xpos);
    LCD_WriteReg(lcddev.setycmd, Ypos);
}
//设置LCD的自动扫描方向
//注意:其他函数可能会受到此函数设置的影响(尤其是9341/6804这两个奇葩),
//所以,一般设置为L2R_U2D即可,如果设置为其他扫描方式,可能导致显示不正常.
//dir:0~7,代表8个方向(具体定义见lcd.h)
//9320/9325/9328/4531/4535/1505/b505/5408/9341/5310/5510/1963等IC已经实际测试
void LCD_Scan_Dir(uint8_t dir)
{
    uint16_t regval=0;
    uint16_t dirreg=0;

    switch(dir)
    {
        case L2R_U2D://从左到右,从上到下
            regval|=(1<<5)|(1<<4)|(0<<3);
            break;
        case L2R_D2U://从左到右,从下到上
            regval|=(0<<5)|(1<<4)|(0<<3);
            break;
        case R2L_U2D://从右到左,从上到下
            regval|=(1<<5)|(0<<4)|(0<<3);
            break;
        case R2L_D2U://从右到左,从下到上
            regval|=(0<<5)|(0<<4)|(0<<3);
            break;
        case U2D_L2R://从上到下,从左到右
            regval|=(1<<5)|(1<<4)|(1<<3);
            break;
        case U2D_R2L://从上到下,从右到左
            regval|=(1<<5)|(0<<4)|(1<<3);
            break;
        case D2U_L2R://从下到上,从左到右
            regval|=(0<<5)|(1<<4)|(1<<3);
            break;
        case D2U_R2L://从下到上,从右到左
            regval|=(0<<5)|(0<<4)|(1<<3);
            break;
    }
    dirreg=0X03;
    regval|=1<<12;
    LCD_WriteReg(dirreg,regval);
}

//设置LCD显示方向
//dir:0,竖屏；1,横屏
void LCD_Display_Dir(uint8_t dir)
{
    if(dir==0)			//竖屏
    {
        lcddev.dir=0;	//竖屏
        lcddev.width=240;
        lcddev.height=320;

        lcddev.wramcmd=0X22;
        lcddev.setxcmd=0X20;
        lcddev.setycmd=0X21;
    }else 				//横屏
    {
        lcddev.dir=1;	//横屏
        lcddev.width=320;
        lcddev.height=240;

        lcddev.wramcmd=0X22;
        lcddev.setxcmd=0X21;
        lcddev.setycmd=0X20;
    }
    LCD_Scan_Dir(DFT_SCAN_DIR);	//默认扫描方向
}

//清屏函数
//color:要清屏的填充色
void LCD_Clear(uint16_t color)
{
    uint32_t index=0;
    uint32_t totalpoint=lcddev.width;
    totalpoint*=lcddev.height; 			//得到总点数
    if((lcddev.id==0X6804)&&(lcddev.dir==1))//6804横屏的时候特殊处理
    {
        lcddev.dir=0;
        lcddev.setxcmd=0X2A;
        lcddev.setycmd=0X2B;
        LCD_SetCursor(0x00,0x0000);		//设置光标位置
        lcddev.dir=1;
        lcddev.setxcmd=0X2B;
        lcddev.setycmd=0X2A;
    }else LCD_SetCursor(0x00,0x0000);	//设置光标位置
    LCD_WriteRAM_Prepare();     		//开始写入GRAM
    for(index=0;index<totalpoint;index++)
    {
        LCD->LCD_RAM=color;
    }
}
//画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(uint16_t x,uint16_t y)
{
    LCD_SetCursor(x,y);		//设置光标位置
    LCD_WriteRAM_Prepare();	//开始写入GRAM
    LCD->LCD_RAM=POINT_COLOR;
}

//在指定区域内填充指定颜色块			 
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)   
//color:要填充的颜色
void LCD_Color_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t *color)
{
    uint16_t height,width;
    uint16_t i,j;
    width=ex-sx+1; 			//得到填充的宽度
    height=ey-sy+1;			//高度
    for(i=0;i<height;i++)
    {
        LCD_SetCursor(sx,sy+i);   	//设置光标位置 
        LCD_WriteRAM_Prepare();     //开始写入GRAM
        for(j=0;j<width;j++)LCD->LCD_RAM=color[i*width+j];//写入数据 
    }
}
//画线
//x1,y1:起点坐标
//x2,y2:终点坐标  
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    uint16_t t;
    int xerr=0,yerr=0,delta_x,delta_y,distance;
    int incx,incy,uRow,uCol;
    delta_x=x2-x1; //计算坐标增量 
    delta_y=y2-y1;
    uRow=x1;
    uCol=y1;
    if(delta_x>0)incx=1; //设置单步方向 
    else if(delta_x==0)incx=0;//垂直线 
    else {incx=-1;delta_x=-delta_x;}
    if(delta_y>0)incy=1;
    else if(delta_y==0)incy=0;//水平线 
    else{incy=-1;delta_y=-delta_y;}
    if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
    else distance=delta_y;
    for(t=0;t<=distance+1;t++ )//画线输出 
    {
        LCD_DrawPoint(uRow,uCol);//画点 
        xerr+=delta_x ;
        yerr+=delta_y ;
        if(xerr>distance)
        {
            xerr-=distance;
            uRow+=incx;
        }
        if(yerr>distance)
        {
            yerr-=distance;
            uCol+=incy;
        }
    }
}
//画矩形	  
//(x1,y1),(x2,y2):矩形的对角坐标
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    LCD_DrawLine(x1,y1,x2,y1);
    LCD_DrawLine(x1,y1,x1,y2);
    LCD_DrawLine(x1,y2,x2,y2);
    LCD_DrawLine(x2,y1,x2,y2);
}

//lcd 复位
static void LCD_Rst(void) {
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET);
    HAL_Delay(5);
    HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET);
}

void LCD_Init(void) {
    LCD_Rst();

    /* 读取lcd的ID */
    LCD_WR_REG(0x0000);        //写入要读的寄存器序号
    lcddev.id = LCD_RD_DATA();        //返回读到的值
    DBG("lcd_id = %x\n", lcddev.id);
    /* 显示屏上面的丝印是ILI9325， 读取到的是5408 */
    /* 接下来就用5408去初始化 */

    LCD_WriteReg(0x00EC,0x108F);// internal timeing
    LCD_WriteReg(0x00EF,0x1234);// ADD
    //LCD_WriteReg(0x00e7,0x0010);
    //LCD_WriteReg(0x0000,0x0001);//开启内部时钟
    LCD_WriteReg(0x0001,0x0100);
    LCD_WriteReg(0x0002,0x0700);//电源开启
    //LCD_WriteReg(0x0003,(1<<3)|(1<<4) ); 	//65K  RGB
    //DRIVE TABLE(寄存器 03H)
    //BIT3=AM BIT4:5=ID0:1
    //AM ID0 ID1   FUNCATION
    // 0  0   0	   R->L D->U
    // 1  0   0	   D->U	R->L
    // 0  1   0	   L->R D->U
    // 1  1   0    D->U	L->R
    // 0  0   1	   R->L U->D
    // 1  0   1    U->D	R->L
    // 0  1   1    L->R U->D 正常就用这个.
    // 1  1   1	   U->D	L->R
    //LCD_WriteReg(0x0003,(1<<12)|(3<<4)|(0<<3) );//65K
    LCD_WriteReg(0x0003, 0x1018);
    LCD_WriteReg(0x0007, 0x0173); /* 262K color and display ON */

    LCD_WriteReg(0x0004,0x0000);
    LCD_WriteReg(0x0008,0x0202);
    LCD_WriteReg(0x0009,0x0000);
    LCD_WriteReg(0x000a,0x0000);//display setting
    LCD_WriteReg(0x000c,0x0001);//display setting
    LCD_WriteReg(0x000d,0x0000);//0f3c
    LCD_WriteReg(0x000f,0x0000);
    //电源配置
    LCD_WriteReg(0x0010,0x0000);
    LCD_WriteReg(0x0011,0x0007);
    LCD_WriteReg(0x0012,0x0000);
    LCD_WriteReg(0x0013,0x0000);
    LCD_WriteReg(0x0007,0x0001);
//    HAL_Delay(5);
    LCD_WriteReg(0x0010,0x1490);
    LCD_WriteReg(0x0011,0x0227);
//    HAL_Delay(5);
    LCD_WriteReg(0x0012,0x008A);
//    HAL_Delay(5);
    LCD_WriteReg(0x0013,0x1a00);
    LCD_WriteReg(0x0029,0x0006);
    LCD_WriteReg(0x002b,0x000d);
//    HAL_Delay(5);
    LCD_WriteReg(0x0020,0x0000);
    LCD_WriteReg(0x0021,0x0000);
//    HAL_Delay(5);
    //伽马校正
    LCD_WriteReg(0x0030,0x0000);
    LCD_WriteReg(0x0031,0x0604);
    LCD_WriteReg(0x0032,0x0305);
    LCD_WriteReg(0x0035,0x0000);
    LCD_WriteReg(0x0036,0x0C09);
    LCD_WriteReg(0x0037,0x0204);
    LCD_WriteReg(0x0038,0x0301);
    LCD_WriteReg(0x0039,0x0707);
    LCD_WriteReg(0x003c,0x0000);
    LCD_WriteReg(0x003d,0x0a0a);
//    HAL_Delay(5);
    LCD_WriteReg(0x0050,0x0000); //水平GRAM起始位置
    LCD_WriteReg(0x0051,0x00ef); //水平GRAM终止位置
    LCD_WriteReg(0x0052,0x0000); //垂直GRAM起始位置
    LCD_WriteReg(0x0053,0x013f); //垂直GRAM终止位置

    LCD_WriteReg(0x0060,0xa700);
    LCD_WriteReg(0x0061,0x0001);
    LCD_WriteReg(0x006a,0x0000);
    LCD_WriteReg(0x0080,0x0000);
    LCD_WriteReg(0x0081,0x0000);
    LCD_WriteReg(0x0082,0x0000);
    LCD_WriteReg(0x0083,0x0000);
    LCD_WriteReg(0x0084,0x0000);
    LCD_WriteReg(0x0085,0x0000);

    LCD_WriteReg(0x0090,0x0010);
    LCD_WriteReg(0x0092,0x0600);
    //开启显示设置
    LCD_WriteReg(0x0007,0x0133);

    LCD_Display_Dir(0);		//默认为竖屏
    LCD_Clear(LCD_COLOR_WHITE);

    LCD_DrawRectangle(20, 20, 80, 80);
}